package org.jsola.hr.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.jsola.admin.entity.HeaderFieldsTemplateDO;
import org.jsola.common.DateKit8;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.DateUtils;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.common.ExcelKit;
import org.jsola.hr.common.HrLogKit;
import org.jsola.hr.constant.EmpSocStatus;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.constant.SocPayCostType;
import org.jsola.hr.dao.IEmpSocItemDAO;
import org.jsola.hr.dao.IEmpSocMonthDAO;
import org.jsola.hr.dao.IEmpSocPlanDAO;
import org.jsola.hr.dao.ISocPlanDispatchUserDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.entity.*;
import org.jsola.hr.provider.IAdminProviderService;
import org.jsola.hr.query.EmpSocPlanQuery;
import org.jsola.hr.query.IncreaseDecreaseQuery;
import org.jsola.hr.query.SocPlanDispatchUserQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.service.excel.ImportDecreaseListener;
import org.jsola.hr.service.excel.ImportIncreaseListener;
import org.jsola.hr.service.notice.ISendNoticeService;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.hr.constant.EmpSocConstant.NO_CONFIRM;
import static org.jsola.hr.constant.EmpSocStatus.*;
import static org.jsola.hr.constant.HrConstants.*;

/**
 * 增减员计划表
 *
 * @author zhr
 */
@Service("hrEmpSocPlanServiceImpl")
@Slf4j
public class EmpSocPlanServiceImpl implements IEmpSocPlanService {

    @Autowired
    private IEmpSocPlanDAO empSocPlanDAO;

    @Autowired
    private ISocProgService socProgService;

    @Autowired
    private IEmpSocTransformService empSocTransformService;

    @Autowired
    private IEmpSocRelatedService empSocRelatedService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IEmpSocMonthService empSocMonthService;

    @Autowired
    private IEmpSocMonthDAO empSocMonthDAO;

    @Autowired
    private ICostBillService costBillService;

    @Autowired
    private IEmpSocPlanItemService empSocPlanItemService;

    @Autowired
    private ISendNoticeService sendNoticeService;

    @Autowired
    private IEmpSocInfoService empSocInfoService;

    @Autowired
    private IEmpSocItemDAO empSocItemDAO;

    @Autowired
    private IEmpSocItemService empSocItemService;

    @Autowired
    private ISocProgItemService socProgItemService;

    @Autowired
    @Lazy
    private IEmpSocInfoExtraService empSocInfoExtraService;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private ITreatmentDisposeEventService treatmentDisposeEventService;

    @Autowired
    private ISocPlanDispatchUserDAO socPlanDispatchUserDAO;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public EmpSocPlanVO save(EmpSocPlanAddDTO empSocPlanAddDTO, TokenUser tokenUser) {
        // 转DO
        EmpSocPlanDO empSocPlanDO = empSocPlanAddDTO.to(EmpSocPlanDO.class);
        // 保存
        empSocPlanDO = save(empSocPlanDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return empSocPlanDO.to(EmpSocPlanVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(EmpSocPlanUpdateDTO empSocPlanUpdateDTO, TokenUser tokenUser) {
        //转DO
        EmpSocPlanDO empSocPlanDO = empSocPlanUpdateDTO.to(EmpSocPlanDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(empSocPlanDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... empSocPlanIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) empSocPlanIds);
    }


    @Override
    public EmpSocPlanVO selectById(String empSocPlanId, String siteId) {
        EmpSocPlanDO empSocPlanDO = selectDOById(empSocPlanId, siteId);
        if (empSocPlanDO == null) {
            return null;
        }
        return empSocPlanDO.to(EmpSocPlanVO.class);
    }

    @Override
    public List<EmpSocPlanListVO> select(EmpSocPlanQuery empSocPlanQuery, String siteId) {
        List<EmpSocPlanDO> empSocPlanDOList = selectDO(empSocPlanQuery, siteId);
        if (CollectionUtils.isEmpty(empSocPlanDOList)) {
            return empSocPlanDOList == null ? null : new ArrayList<>();
        }
        return empSocPlanDOList.stream()
                .map(empSocPlanDO -> empSocPlanDO.to(EmpSocPlanListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(EmpSocPlanQuery empSocPlanQuery, String siteId) {
        Example example = buildExample(empSocPlanQuery, siteId);
        return empSocPlanDAO.selectCountByExample(example);
    }

    @Override
    public Page<EmpSocPlanListVO> selectPage(EmpSocPlanQuery empSocPlanQuery, String siteId) {
        Example example = buildExample(empSocPlanQuery, siteId);
        Page<EmpSocPlanDO> page = empSocPlanDAO.selectPageByExample(example,
                empSocPlanQuery.getPageNo(),
                empSocPlanQuery.getPageSize());

        return page.to(EmpSocPlanListVO.class);
    }

    @Override
    public EmpSocPlanDO selectDOById(String empSocPlanId, String siteId) {
        return listById(empSocPlanId, siteId);
    }

    @Override
    public List<EmpSocPlanDO> selectByIdList(List<String> planIdList, String siteId) {
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andIn("id", planIdList);
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocPlanDO> selectDO(EmpSocPlanQuery empSocPlanQuery, String siteId) {
        Example example = buildExample(empSocPlanQuery, siteId);
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public String increase(EmpSocPlanBatchAddDTO batchIncreaseDTO, TokenUser tokenUser) {
        ParamException.notEmpty(batchIncreaseDTO.getEmpSocPlanAddDTOList(), "请选择参保人员");
        String companyId = batchIncreaseDTO.getCompanyId();
        String year = batchIncreaseDTO.getYear();
        String month = batchIncreaseDTO.getMonth();
        SocProgDO socProgDO = socProgService.selectDOById(batchIncreaseDTO.getSchemeId(), tokenUser.getSiteId());
        EmpSocPlanBatchAddDTO batchAddDTO = batchCalculationAmount(batchIncreaseDTO, socProgDO);
        // 增员列表
        List<EmpSocPlanAddDTO> empSocPlanAddDTOList = batchAddDTO.getEmpSocPlanAddDTOList();
        List<String> empIdList = empSocPlanAddDTOList.stream()
                .map(EmpSocPlanAddDTO::getEmpId).distinct().collect(Collectors.toList());
        // 查询这些员工的社保相关信息和花名册相关信息
        List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService
                .selectByEmpIdList(empIdList, tokenUser.getSiteId());
        Map<String, EmpSocRelatedDO> empSocRelatedMap = empSocRelatedList.stream()
                .collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
        List<EmpInfoDO> empInfoDOList = empInfoService.selectDoByIds(empIdList);
        Map<String, EmpInfoDO> empInfoMap = empInfoDOList.stream()
                .collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
        // 校验重复增员
        this.checkRepeatIncrease(empSocRelatedMap, empInfoMap, batchIncreaseDTO.getEmpSocPlanAddDTOList());
        Date increaseDate = DateKit8.parse(year + "-" + month + "-01", "yyyy-MM-dd");
        // 判断一下是不是待增
        boolean plan = !CollectionUtils.isEmpty(DateUtils.getMonthDifferences(new Date(), increaseDate));
        EmpSocMonthDO empSocMonth = empSocMonthService
                .selectMonthByYearAndMonth(companyId, year, month, tokenUser.getSiteId());
        if (!ObjectUtils.isEmpty(empSocMonth)) {
            if (costBillService.checkMonthIdCreateCostBill(empSocMonth.getId(), tokenUser.getSiteId())) {
                throw new ParamException(month + "已创建账单不可进行增员操作");
            }
        }
        // 记录一下增员的人数,第一个人的姓名和员工Id,便于后面记录日志
        int saveCount = (int) empSocPlanAddDTOList.stream().map(EmpSocPlanAddDTO::getEmpId).distinct().count();
        String empName = empInfoDOList.get(0).getName();
        String empId = empInfoDOList.get(0).getId();
        List<EmpSocRelatedDO> updateList = new ArrayList<>();
        for (EmpSocPlanAddDTO empSocPlanAddDTO : empSocPlanAddDTOList) {
            EmpSocRelatedDO empSocRelatedDO = empSocRelatedMap.get(empSocPlanAddDTO.getEmpId());
            EmpInfoDO empInfo = empInfoMap.get(empSocPlanAddDTO.getEmpId());
            empSocRelatedDO.setSchemeId(empSocPlanAddDTO.getSchemeId());
            // 增加增员记录
            EmpSocPlanDO empSocPlanDO = empSocPlanAddDTO.to(EmpSocPlanDO.class);
            // 本来记录是需要确认才能增员成功,现在不需要这个逻辑了,默认直接成功
            empSocPlanDO.setFeedbackFlag(1);
            // 这里的  EmpSocState  不是特指社保状态
            empSocPlanDO.setEmpSocState(plan ? EmpSocStatus.WAIT_INCREMENT.getValue() : EmpSocStatus.INCREMENT.getValue());
            // 更新员工增员时间
            empSocRelatedService.setIncreaseDateByType(empSocPlanAddDTO.getType(), empSocRelatedDO, increaseDate);
            empSocRelatedService.setAccountByScheme(empSocPlanAddDTO.getType(), empSocRelatedDO, socProgDO);
            // 这里有可能是一个 empSocRelatedDO 会出现三次 更新三次太麻烦了 这里 记录一下需要更新的数据 拿出去更新,也会缩短执行时间
            if (!updateList.contains(empSocRelatedDO)) {
                updateList.add(empSocRelatedDO);
            }
            empSocPlanDO = save(empSocPlanDO, tokenUser.getSiteId(), tokenUser.getUserId());
            // 保存员工参保方案子项
            List<EmpSocPlanItemAddDTO> empSocPlanItemAddDTOList = empSocPlanAddDTO.getEmpSocPlanItemAddDTOList();
            ParamException.notEmpty(empSocPlanItemAddDTOList, "员工参保子项不能为空");
            String planId = empSocPlanDO.getId();
            List<EmpSocPlanItemDO> empSocPlanItemList = empSocPlanItemAddDTOList.stream().map(
                    empSocPlanItemAddDTO -> {
                        EmpSocPlanItemDO empSocPlanItemDO = empSocPlanItemAddDTO.to(EmpSocPlanItemDO.class);
                        empSocPlanItemDO.setId(null);
                        empSocPlanItemDO.setPlanId(planId);
                        empSocPlanItemDO.preInsert(tokenUser.getUserId());
                        empSocPlanItemDO.setSiteId(tokenUser.getSiteId());
                        return empSocPlanItemDO;
                    }
            ).collect(Collectors.toList());
            empSocPlanItemService.bachSave(empSocPlanItemList);
            // 如果 empSocMonth 为空说明该月份还没有创建  不用生成参保记录
            // 后面如果他点添加次月账单的时候  会很根据增减员计划  去自动生成参保记录
            if (!ObjectUtils.isEmpty(empSocMonth)) {
                increaseMerge(empSocPlanItemList, empSocMonth, empInfo, empSocPlanDO, tokenUser, false);
                // 查询是否有未来月
                List<EmpSocMonthDO> monthList = empSocMonthDAO.selectMonthIdListAfterDate(year + month,
                        companyId, tokenUser.getSiteId());
                if (!CollectionUtils.isEmpty(monthList)) {
                    increaseFutureMerge(empSocPlanItemList, monthList, empInfo, empSocPlanDO, tokenUser);
                    // 更新员工社保信息
                    empSocRelatedService.setNormalStatus(empSocRelatedDO, empSocPlanDO.getType(), tokenUser);
                    if (!updateList.contains(empSocRelatedDO)) {
                        updateList.add(empSocRelatedDO);
                    }
                }
            }
        }
        // 最后把员工更新一下
        for (EmpSocRelatedDO empSocRelatedDO : updateList) {
            empSocRelatedService.updateByIdSelective(empSocRelatedDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        if (saveCount > 1) {
            HrLogKit.logForSave("由管理员" + tokenUser.getUserName() + "进行社保增员 增员" + empName
                    + "等" + saveCount + "名员工", "EmpSocInfo", companyId, companyId, tokenUser);
        } else {
            HrLogKit.logForSave("由管理员" + tokenUser.getUserName() + "进行社保增员 增员员工" + empName,
                    "EmpSocInfo", companyId, companyId, tokenUser);
        }
        try {
            sendNoticeService.sendIncreaseDecreaseNotice(empId, companyId, saveCount, true);
        } catch (Exception e) {
            log.error("增员发送通知失败");
        }
        return batchIncreaseDTO.getYear() + "年" + batchIncreaseDTO.getMonth() + "月";
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<CheckDecreaseFailVO> decrease(IncreaseDecreaseQuery increaseDecreaseQuery, TokenUser tokenUser) {
        List<CheckDecreaseFailVO> checkDecreaseFailList = new ArrayList<>();
        List<String> decreaseEmpIdList = increaseDecreaseQuery.getEmpIdList();
        ParamException.notEmpty(decreaseEmpIdList, "未选择减员人员");
        List<EmpInfoDO> empInfoList = empInfoService.selectDoByIds(decreaseEmpIdList);
        if (CollectionUtils.isEmpty(empInfoList)) {
            throw new ParamException("员工已被删除");
        }
        Map<String, EmpInfoDO> empInfoMap = empInfoList.stream()
                .collect(Collectors.toMap(EmpInfoDO::getId, v -> v));
        // 记录减员人数和第一个人员的名字和Id最后记录日志和发通知用
        int decreaseCount = decreaseEmpIdList.size();
        String tempEmpName = empInfoList.get(0).getName();
        String tempEmpId = empInfoList.get(0).getId();
        // 校验传进来的这些人可不可以减员,不可以的返回前端,不报错
        List<CheckDecreaseVO> checkDecreaseList = checkDecrease(decreaseEmpIdList, tokenUser);
        Map<String, CheckDecreaseVO> checkDecreaseMap = checkDecreaseList.stream()
                .collect(Collectors.toMap(CheckDecreaseVO::getEmpId, v -> v));
        List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService
                .selectByEmpIdList(decreaseEmpIdList, tokenUser.getSiteId());
        Map<String, EmpSocRelatedDO> empSocRelatedMap = empSocRelatedList.stream()
                .collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
        String companyId = increaseDecreaseQuery.getCompanyId();
        List<EmpSocPlanDO> insertList = new ArrayList<>();
        List<String> schemeIdList = empSocRelatedList.stream()
                .map(EmpSocRelatedDO::getSchemeId).collect(Collectors.toList());
        List<SocProgDO> schemeList = socProgService.selectByIdList(schemeIdList, tokenUser);
        Map<String, SocProgDO> schemeMap = schemeList.stream().collect(Collectors.toMap(SocProgDO::getId, v -> v));
        // 未完结的待遇处理事件也会报错
        List<TreatmentDisposeEventDO> eventList = treatmentDisposeEventService
                .findNotFinishedEventByEmpIdList(decreaseEmpIdList, tokenUser);
        List<String> eventEmpIdList = eventList.stream()
                .map(TreatmentDisposeEventDO::getEmpId).distinct().collect(Collectors.toList());
        for (EmpSocRelatedDO empSocRelatedDO : empSocRelatedList) {
            SocProgDO scheme = schemeMap.get(empSocRelatedDO.getSchemeId());
            EmpInfoDO empInfoDO = empInfoMap.get(empSocRelatedDO.getEmpId());
            CheckDecreaseVO checkDecreaseVO = checkDecreaseMap.get(empSocRelatedDO.getEmpId());
            CheckDecreaseFailVO checkDecreaseFailVO = new CheckDecreaseFailVO();
            checkDecreaseFailVO.setEmpId(empInfoDO.getId());
            checkDecreaseFailVO.setEmpName(empInfoDO.getName());
            if (!ObjectUtils.isEmpty(scheme)) {
                checkDecreaseFailVO.setSchemeName(scheme.getProgName());
                checkDecreaseFailVO.setSocCityName(scheme.getSocCityName());
            }
            if (increaseDecreaseQuery.getStopSoc()) {
                if (checkDecreaseVO.getSocNoPayment()) {
                    checkDecreaseFailVO.setSocDescription("无参保记录或已减员");
                } else if (eventEmpIdList.contains(empSocRelatedDO.getEmpId())) {
                    checkDecreaseFailVO.setSocDescription("待遇处理存在未完结事件");
                } else {
                    String stopSocYear = increaseDecreaseQuery.getPlanStopSocYear();
                    String stopSocMonth = increaseDecreaseQuery.getPlanStopSocMonth();
                    String dateStr = stopSocYear + "-" + stopSocMonth;
                    checkDecreaseFailVO.setSocDescription(dateStr);
                    // 创建一条减员记录
                    insertList.add(initDecreasePlanByType(companyId, SOC_ITEM_TYPE, stopSocYear, stopSocMonth,
                            empSocRelatedDO.getEmpId(), increaseDecreaseQuery.getServiceCost(), tokenUser));
                    Date stopSocDate = DateKit8.parseDate(dateStr + "-01");
                    // 更新员工的减员时间和状态
                    empSocRelatedService.setStopDateByType(empSocRelatedDO, SOC_ITEM_TYPE, stopSocDate);
                }
            }
            if (increaseDecreaseQuery.getStopMe()) {
                if (checkDecreaseVO.getMeNoPayment()) {
                    checkDecreaseFailVO.setMeDescription("无参保记录或已减员");
                } else if (eventEmpIdList.contains(empSocRelatedDO.getEmpId())) {
                    checkDecreaseFailVO.setSocDescription("待遇处理存在未完结事件");
                } else {
                    String stopMeYear = increaseDecreaseQuery.getPlanStopMeYear();
                    String stopMeMonth = increaseDecreaseQuery.getPlanStopMeMonth();
                    String dateStr = stopMeYear + "-" + stopMeMonth;
                    checkDecreaseFailVO.setMeDescription(dateStr);
                    // 创建一条减员记录
                    insertList.add(initDecreasePlanByType(companyId, ME_ITEM_TYPE, stopMeYear, stopMeMonth,
                            empSocRelatedDO.getEmpId(), increaseDecreaseQuery.getServiceCost(), tokenUser));
                    Date planStopMeDate = DateKit8.parseDate(dateStr + "-01");
                    // 更新员工的减员时间和状态
                    empSocRelatedService.setStopDateByType(empSocRelatedDO, ME_ITEM_TYPE, planStopMeDate);
                }
            }
            if (increaseDecreaseQuery.getStopPf()) {
                if (checkDecreaseVO.getPfNoPayment()) {
                    checkDecreaseFailVO.setPfDescription("无参保记录或已减员");
                } else if (eventEmpIdList.contains(empSocRelatedDO.getEmpId())) {
                    checkDecreaseFailVO.setSocDescription("待遇处理存在未完结事件");
                } else {
                    String sealedDateYear = increaseDecreaseQuery.getSealedDateYear();
                    String sealedDateMonth = increaseDecreaseQuery.getSealedDateMonth();
                    String dateStr = sealedDateYear + "-" + sealedDateMonth;
                    checkDecreaseFailVO.setPfDescription(dateStr);
                    // 创建一条减员记录
                    insertList.add(initDecreasePlanByType(companyId, PF_ITEM_TYPE, sealedDateYear, sealedDateMonth,
                            empSocRelatedDO.getEmpId(), increaseDecreaseQuery.getServiceCost(), tokenUser));
                    Date sealedDateDate = DateKit8.parseDate(dateStr + "-01");
                    // 更新员工的计划时间和状态
                    empSocRelatedService.setStopDateByType(empSocRelatedDO, PF_ITEM_TYPE, sealedDateDate);
                }
            }
            checkDecreaseFailList.add(checkDecreaseFailVO);
            empSocRelatedService.updateByIdSelective(empSocRelatedDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        if (!CollectionUtils.isEmpty(insertList)) {
            empSocPlanDAO.insertListAndSetId(insertList);
            for (EmpSocPlanDO empSocPlanDO : insertList) {
                String monthId = empSocMonthService.selectMonthIdByYearAndMonth(companyId,
                        empSocPlanDO.getYear(), empSocPlanDO.getMonth(), tokenUser.getSiteId());
                decreaseMerge(monthId, empSocPlanDO, false, tokenUser);
                List<EmpSocMonthDO> monthList = empSocMonthDAO.selectMonthIdListAfterDate(empSocPlanDO.getYear() +
                        empSocPlanDO.getMonth(), companyId, tokenUser.getSiteId());
                if (!CollectionUtils.isEmpty(monthList)) {
                    for (EmpSocMonthDO empSocMonth : monthList) {
                        decreaseMerge(empSocMonth.getId(), empSocPlanDO, true, tokenUser);
                    }
                    EmpSocRelatedDO empSocRelatedDO = empSocRelatedMap.get(empSocPlanDO.getEmpId());
                    // 更新员工社保信息
                    empSocRelatedService.updateStopStatus(empSocRelatedDO, empSocPlanDO.getType(), tokenUser);
                }
            }
        }
        for (String empId : decreaseEmpIdList) {
            // 判断一下这个月这个人是否已离职
            EmpInfoDO empInfo = empInfoMap.get(empId);
            Date leaveDate = empInfo.getLeaveDate();
            if (!ObjectUtils.isEmpty(leaveDate)) {
                String yearMonthStr = DateUtils.getYearMonthStr(leaveDate);
                List<String> monthIdList = empSocMonthService
                        .selectMonthIdListAfterAndEqualsDate(companyId, yearMonthStr, tokenUser.getSiteId());
                empSocInfoService.updateWhetherLeave(Collections.singletonList(empId),
                        true, monthIdList, tokenUser);
            }
        }
        if (decreaseCount > 1) {
            HrLogKit.logForSave("由管理员" + tokenUser.getUserName() + "进行减员 减员" + tempEmpName
                    + "等" + decreaseCount + "名员工", "EmpSocInfo", companyId, companyId, tokenUser);
        } else {
            HrLogKit.logForSave("由管理员" + tokenUser.getUserName() + "进行减员 减员员工" + tempEmpName,
                    "EmpSocInfo", companyId, companyId, tokenUser);
        }
        try {
            sendNoticeService.sendIncreaseDecreaseNotice(tempEmpId, companyId, decreaseCount, true);
        } catch (Exception e) {
            log.error("减员发送通知失败");
        }
        return checkDecreaseFailList;
    }

    @Override
    public Page<EmpSocPlanListVO> increaseOrDecreaseList(EmpSocPlanQuery empSocPlanQuery, TokenUser tokenUser) {
        if (!ObjectUtils.isEmpty(empSocPlanQuery.getPlanIncreaseOrDecreaseDate())) {
            empSocPlanQuery.setDateStr(DateUtils.getYearMonthStr(empSocPlanQuery.getPlanIncreaseOrDecreaseDate()));
        }
        PageInfo<EmpSocPlanListVO> resultPage = PageHelper.startPage(empSocPlanQuery.getPageNo(),
                empSocPlanQuery.getPageSize()).doSelectPageInfo(() ->
                empSocPlanDAO.increaseDecreaseList(empSocPlanQuery, tokenUser.getSiteId()));
        if (CollectionUtils.isEmpty(resultPage.getList())) {
            return new Page<>(new ArrayList<>(), empSocPlanQuery.getPageNo(),
                    empSocPlanQuery.getPageSize(), (int) resultPage.getTotal());
        } else {
            for (EmpSocPlanListVO empSocPlan : resultPage.getList()) {
                empSocPlan.setPlanDate(empSocPlan.getYear() + "-" + empSocPlan.getMonth());
                List<EmpSocPlanItemDO> itemList = empSocPlanItemService.selectByPlanId(empSocPlan.getId(), tokenUser);
                Map<String, Object> data = new HashMap<>();
                if (!CollectionUtils.isEmpty(itemList)) {
                    empSocPlan.setItemList(itemList);
                    for (EmpSocPlanItemDO empSocPlanItem : itemList) {
                        data.put(empSocPlanItem.getItemName() + "基数", empSocPlanItem.getCardinalityDefault());
                    }
                }
                empSocPlan.setData(data);
            }
        }
        return new Page<>(resultPage.getList(), empSocPlanQuery.getPageNo(),
                empSocPlanQuery.getPageSize(), (int) resultPage.getTotal());
    }

    @Override
    public List<KeyNameVO> increaseOrDecreaseListHead(EmpSocPlanQuery empSocPlanQuery, TokenUser tokenUser) {
        List<KeyNameVO> result = new ArrayList<>();
        List<HeaderFieldsTemplateDO> itemHeaderList = adminProviderService
                .selectByCodeAndGroup("socPlanHead", "表头");
        for (HeaderFieldsTemplateDO headerField : itemHeaderList) {
            KeyNameVO keyName = new KeyNameVO();
            keyName.setKey(headerField.getCode());
            keyName.setName(headerField.getName());
            result.add(keyName);
        }
        List<EmpSocPlanItemVO> itemList;
        if (!StringUtils.isEmpty(empSocPlanQuery.getCompanyId())) {
            itemList = empSocPlanItemService
                    .selectByCompanyId(empSocPlanQuery.getCompanyId(), tokenUser);
        } else {
            List<EmpSocPlanListVO> empSocPlanList = socPlanDispatchUserDAO
                    .taskOrderUser(new SocPlanDispatchUserQuery(), tokenUser.getSiteId(), tokenUser.getUserId());
            List<String> planIdList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(empSocPlanList)) {
                planIdList = empSocPlanList.stream().map(EmpSocPlanListVO::getId).collect(Collectors.toList());
            }
            itemList = empSocPlanItemService.selectByPlanIds(planIdList, tokenUser);
        }
        if (!CollectionUtils.isEmpty(itemList)) {
            List<String> itemNameList = itemList.stream()
                    .map(EmpSocPlanItemVO::getItemName).distinct().collect(Collectors.toList());
            for (String item : itemNameList) {
                KeyNameVO keyName = new KeyNameVO();
                keyName.setKey(item + "基数");
                keyName.setName(item + "基数");
                result.add(keyName);
            }
        }
        return result;
    }

    @Override
    public EmpSocTransformCountVO increaseDecreaseListCount(EmpSocPlanQuery empSocPlanQuery, TokenUser tokenUser) {
        EmpSocTransformCountVO resultCount = new EmpSocTransformCountVO();
        List<EmpSocPlanListVO> empSocPlanList = empSocPlanDAO.increaseDecreaseList(empSocPlanQuery, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(empSocPlanList)) {
            return resultCount;
        }
        resultCount.setAllCount(empSocPlanList.size());
        Map<Integer, Long> empSocStatusGroup = empSocPlanList.stream().
                collect(Collectors.groupingBy(EmpSocPlanListVO::getEmpSocState, Collectors.counting()));
        // 增员人数
        Long incrementValue = empSocStatusGroup.get(INCREMENT.getValue());
        resultCount.setIncreaseCount(ObjectUtils.isEmpty(incrementValue) ?
                0 : Math.toIntExact(incrementValue));
        // 待增
        resultCount.setWaitIncreaseCount(empSocStatusGroup.get(WAIT_INCREMENT.getValue()));
        // 减员
        Long attritionValue = empSocStatusGroup.get(ATTRITION.getValue());
        resultCount.setDecreaseCount(ObjectUtils.isEmpty(attritionValue) ?
                0 : Math.toIntExact(attritionValue));
        // 待减
        resultCount.setWaitDecreaseCount(empSocStatusGroup.get(WAIT_ATTRITION.getValue()));
        return resultCount;
    }

    @Override
    public List<CheckDecreaseVO> checkDecrease(List<String> decreaseEmpIdList, TokenUser tokenUser) {
        List<CheckDecreaseVO> resultList = new ArrayList<>();
        if (CollectionUtils.isEmpty(decreaseEmpIdList)) {
            throw new ParamException("请选择减员人员");
        }
        List<EmpSocRelatedDO> empSocRelatedList = empSocRelatedService
                .selectByEmpIdList(decreaseEmpIdList, tokenUser.getSiteId());
        Map<String, EmpSocRelatedDO> empSocRelatedMap = empSocRelatedList.stream()
                .collect(Collectors.toMap(EmpSocRelatedDO::getEmpId, v -> v));
        List<Integer> checkStatusList = Arrays
                .asList(WAIT_ATTRITION.getValue(), ATTRITION.getValue(), STOP_SOC.getValue(), NOT_SOC.getValue());
        for (String empId : decreaseEmpIdList) {
            EmpSocRelatedDO empSocRelatedDO = empSocRelatedMap.get(empId);
            CheckDecreaseVO checkDecreaseVO = new CheckDecreaseVO();
            checkDecreaseVO.setEmpId(empId);
            Integer socState = empSocRelatedDO.getSocState();
            // 计划减员时间和状态为上述 均不能减员
            Date socPlanStopDate = empSocRelatedDO.getSocPlanStopDate();
            checkDecreaseVO.setSocNoPayment(checkStatusList.contains(socState) || !ObjectUtils.isEmpty(socPlanStopDate));
            Integer meState = empSocRelatedDO.getMeState();
            Date mePlanStopDate = empSocRelatedDO.getMePlanStopDate();
            checkDecreaseVO.setMeNoPayment(checkStatusList.contains(meState) || !ObjectUtils.isEmpty(mePlanStopDate));
            Integer pfState = empSocRelatedDO.getPfState();
            Date pfPlanSealedDate = empSocRelatedDO.getPfPlanSealedDate();
            checkDecreaseVO.setPfNoPayment(checkStatusList.contains(pfState) || !ObjectUtils.isEmpty(pfPlanSealedDate));
            resultList.add(checkDecreaseVO);
        }
        return resultList;
    }

    @Override
    public Integer notFeedbackCount(String companyId, TokenUser tokenUser) {
        List<String> list = empSocPlanDAO.notFeedbackList(companyId, tokenUser.getSiteId());
        return list.size();
    }

    @Override
    public EmpSocPlanDO selectEmpLatestPlan(String year, String month, String empId, String siteId) {
        // feedbackFlag = 1 表示处于反馈成功状态
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("empId", empId)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("feedbackFlag", 1)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        example.orderBy("gmtModified").desc();
        List<EmpSocPlanDO> empSocPlanList = empSocPlanDAO.selectByExample(example);
        if (CollectionUtils.isEmpty(empSocPlanList)) {
            return null;
        }
        return empSocPlanList.get(0);
    }

    @Override
    public List<EmpSocPlanDO> selectPlanByEmpIdList(String year, String month, List<String> empIdList, String siteId) {
        // feedbackFlag = 1 表示处于反馈成功状态
        if (CollectionUtils.isEmpty(empIdList)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andIn("empId", empIdList)
                .andEqualTo("year", year)
                .andEqualTo("month", month)
                .andEqualTo("feedbackFlag", 1)
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public String getPlanType(EmpSocPlanDO empSocPlanDO) {
        switch (empSocPlanDO.getEmpSocState()) {
            case 2:
            case 3:
                return INCREASE_STR;
            case 4:
            case 5:
                return DECREASE_STR;
            default:
                throw new ParamException("人员状态错误");
        }
    }

    @Override
    public List<EmpSocPlanDO> getSuccessPlanByEmp(String empId, TokenUser tokenUser) {
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("empId", empId)
                .andEqualTo("feedbackFlag", 1);
        example.orderBy("gmtCreate").asc();
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public Integer selectEmpCountByScheme(String schemeId) {
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIsNull("feedbackFlag")
                .andEqualTo("schemeId", schemeId);
        return empSocPlanDAO.selectCountByExample(example);
    }

    @Override
    public List<EmpSocPlanDO> selectAllNotFeedback() {
        String[] yearAndMonth = DateUtils.getYearAndMonth(new Date());
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIsNull("feedbackFlag")
                .andEqualTo("year", yearAndMonth[0])
                .andEqualTo("month", yearAndMonth[1]);
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocPlanDO> selectSuccessFeedbackByMonth(EmpSocMonthDO empSocMonthDO, TokenUser tokenUser) {
        return empSocPlanDAO.selectSuccessFeedbackByMonth(empSocMonthDO.getCompanyId(),
                empSocMonthDO.getYear(), empSocMonthDO.getMonth());
    }

    @Override
    public List<EmpSocPlanDO> selectPlanByEmpId(String empId, Integer type, TokenUser tokenUser) {
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", tokenUser.getSiteId())
                .andEqualTo("empId", empId)
                .andEqualTo("type", type)
                .andIsNull("feedbackFlag");
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public ImportExcelResultListVO<ImportIncreaseDTO> importIncrease(File file, String companyId, TokenUser tokenUser) {
        ImportIncreaseListener importIncreaseListener = new ImportIncreaseListener();
        importIncreaseListener.setTokenUser(tokenUser);
        importIncreaseListener.setCompanyId(companyId);
        importIncreaseListener.setParseOnly(false);
        EasyExcel.read(file, importIncreaseListener).sheet("导入增员").doRead();
        ImportExcelResultListVO<ImportIncreaseDTO> result = new ImportExcelResultListVO<>();
        List<ImportIncreaseDTO> errorList = importIncreaseListener.getErrorList();
        List<Map<Integer, String>> dataList = importIncreaseListener.getDataList();
        result.setHeadList(importIncreaseListener.getHeadList());
        result.setErrorList(errorList);
        result.setErrorCount(errorList.size());
        result.setAllCount(dataList.size());
        result.setSuccessCount(dataList.size() - errorList.size());
        return result;
    }

    @Override
    public String exportIncreaseErrorList(ImportExcelResultListVO<ImportIncreaseDTO> errorInfo) {
        List<List<String>> dataList = new ArrayList<>();
        List<List<String>> headList = new ArrayList<>();
        List<String> allHead = errorInfo.getHeadList();
        List<ImportIncreaseDTO> errorListBean = errorInfo.getErrorList();
        for (String oneHead : allHead) {
            List<String> head = new ArrayList<>();
            head.add(oneHead);
            headList.add(head);
        }
        for (ImportIncreaseDTO importIncreaseDTO : errorListBean) {
            List<String> oneDate = new ArrayList<>();
            for (String head : allHead) {
                oneDate.add(importIncreaseDTO.getExcelDate().get(head));
            }
            dataList.add(oneDate);
        }
        return ExcelKit.writeExcelWithModel("增员到如错误表", dataList, headList, false,
                "错误数据", (CellWriteHandler) null);
    }

    @Override
    public ImportExcelResultListVO<ImportDecreaseDTO> importDecrease(File file, String companyId, TokenUser tokenUser) {
        ImportDecreaseListener importDecreaseListener = new ImportDecreaseListener();
        importDecreaseListener.setTokenUser(tokenUser);
        importDecreaseListener.setCompanyId(companyId);
        importDecreaseListener.setParseOnly(false);
        EasyExcel.read(file, ImportDecreaseDTO.class, importDecreaseListener).sheet("导入减员").doRead();
        ImportExcelResultListVO<ImportDecreaseDTO> result = new ImportExcelResultListVO<>();
        List<ImportDecreaseDTO> dataList = importDecreaseListener.getDataList();
        List<ImportDecreaseDTO> failList = importDecreaseListener.getFailList();
        result.setAllCount(dataList.size());
        result.setErrorCount(failList.size());
        result.setSuccessCount(dataList.size() - failList.size());
        result.setErrorList(failList);
        return result;
    }

    @Override
    public String exportDecreaseErrorList(ImportExcelResultListVO<ImportDecreaseDTO> errorInfo) {
        return ExcelKit.exportExcel("decreaseError", "错误列表",
                ImportDecreaseDTO.class, errorInfo.getErrorList());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void importIncreaseSaveData(List<ImportIncreaseDTO> saveDataList, String companyId, TokenUser tokenUser) {
        String confirmSocBySite = empSocInfoExtraService.getConfirmSocBySite(tokenUser);
        // 增员方法是增员同一个方案同一个月才能复用这里只能挨个循环调用了
        // 根据Excel中输入的  方案名称  查询出对应发的方案
        List<String> schemeNameList = saveDataList.stream()
                .map(ImportIncreaseDTO::getSchemeName).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(schemeNameList)) {
            List<SocProgDO> socProgList = socProgService.selectByNameList(schemeNameList, companyId, tokenUser.getSiteId());
            Map<String, SocProgDO> schemeNameMap = socProgList
                    .stream().collect(Collectors.toMap(SocProgDO::getProgName, v -> v));
            List<String> socProgIdList = socProgList
                    .stream().map(SocProgDO::getId).collect(Collectors.toList());
            List<SocProgItemDO> socProgItemList = socProgItemService.selectBySocProgIds(socProgIdList, tokenUser);
            Map<String, List<SocProgItemDO>> itemMap = socProgItemList
                    .stream().collect(Collectors.groupingBy(SocProgItemDO::getProgId));
            saveDataList.parallelStream().forEach(saveData -> {
                String[] yearAndMonth = saveData.getSocDate().split("/");
                EmpSocPlanBatchAddDTO batchIncreaseDTO = new EmpSocPlanBatchAddDTO();
                SocProgDO socProgDO = schemeNameMap.get(saveData.getSchemeName());
                List<SocProgItemDO> itemList = itemMap.get(socProgDO.getId());
                batchIncreaseDTO.setSchemeId(socProgDO.getId());
                batchIncreaseDTO.setCompanyId(companyId);
                batchIncreaseDTO.setYear(yearAndMonth[0]);
                batchIncreaseDTO.setMonth(yearAndMonth[1]);
                List<EmpSocPlanAddDTO> empSocPlanAddList = new ArrayList<>();
                for (String increaseTypeStr : saveData.getTypeList()) {
                    EmpSocPlanAddDTO empSocPlanAddDTO = new EmpSocPlanAddDTO();
                    Integer increaseType = importTypeConverted(increaseTypeStr);
                    List<SocProgItemDO> itemFilterList = itemList.stream()
                            .filter(item -> increaseType.equals(item.getItemType()))
                            .collect(Collectors.toList());
                    List<EmpSocPlanItemAddDTO> itemAddList = itemFilterList.stream().map(socProgItem -> {
                        EmpSocPlanItemAddDTO itemAdd = socProgItem.to(EmpSocPlanItemAddDTO.class);
                        if (itemAdd.getCardinalityDefault().compareTo(BigDecimal.ZERO) == 0) {
                            itemAdd.setCardinalityDefault(socProgItem.getCardinalityDefaultAmount());
                        }
                        String input = saveData.getItemMap().get(socProgItem.getItemName() + "基数");
                        // 如果输入了这个子项的基数就是用他写的这个基数
                        if (!StringUtils.isEmpty(input)) {
                            // 判断键入的基数是否在基数范围内
                            BigDecimal inputCardinality = new BigDecimal(input);
                            if (inputCardinality.compareTo(socProgItem.getCardinalityMin()) < 0) {
                                inputCardinality = socProgItem.getCardinalityMin();
                            } else if (inputCardinality.compareTo(socProgItem.getCardinalityMax()) > 0) {
                                inputCardinality = socProgItem.getCardinalityMax();
                            }
                            itemAdd.setCardinalityDefault(inputCardinality);
                        }
                        return itemAdd;
                    }).collect(Collectors.toList());
                    empSocPlanAddDTO.setEmpId(saveData.getEmpId());
                    empSocPlanAddDTO.setType(increaseType);
                    empSocPlanAddDTO.setCompanyId(companyId);
                    empSocPlanAddDTO.setSchemeId(socProgDO.getId());
                    empSocPlanAddDTO.setServiceCost(new BigDecimal(saveData.getServiceCost()));
                    empSocPlanAddDTO.setYear(yearAndMonth[0]);
                    empSocPlanAddDTO.setMonth(yearAndMonth[1]);
                    empSocPlanAddDTO.setEmpSocPlanItemAddDTOList(itemAddList);
                    empSocPlanAddList.add(empSocPlanAddDTO);
                }
                batchIncreaseDTO.setEmpSocPlanAddDTOList(empSocPlanAddList);
                if (NO_CONFIRM.equals(confirmSocBySite)) {
                    increase(batchIncreaseDTO, tokenUser);
                } else {
                    empSocInfoExtraService.increaseExtra(batchIncreaseDTO, tokenUser);
                }
            });
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void importDecreaseSaveData(List<ImportDecreaseDTO> saveDataList, String companyId, TokenUser tokenUser) {
        String confirmSocBySite = empSocInfoExtraService.getConfirmSocBySite(tokenUser);
        for (ImportDecreaseDTO importDecreaseDTO : saveDataList) {
            IncreaseDecreaseQuery increaseDecreaseQuery = new IncreaseDecreaseQuery();
            increaseDecreaseQuery.setCompanyId(companyId);
            increaseDecreaseQuery.setEmpIdList(Collections.singletonList(importDecreaseDTO.getEmpId()));
            if (!StringUtils.isEmpty(importDecreaseDTO.getServiceCost())) {
                increaseDecreaseQuery.setServiceCost(new BigDecimal(importDecreaseDTO.getServiceCost()));
            }
            if (importDecreaseDTO.getWhetherSocDecrease()) {
                increaseDecreaseQuery.setStopSoc(true);
                String[] yearAndMonth = importDecreaseDTO.getStopSocDate().split("/");
                increaseDecreaseQuery.setPlanStopSocYear(yearAndMonth[0]);
                increaseDecreaseQuery.setPlanStopSocMonth(yearAndMonth[1]);
            } else {
                increaseDecreaseQuery.setStopSoc(false);
            }
            if (importDecreaseDTO.getWhetherMeDecrease()) {
                increaseDecreaseQuery.setStopMe(true);
                String[] yearAndMonth = importDecreaseDTO.getStopMeDate().split("/");
                increaseDecreaseQuery.setPlanStopMeYear(yearAndMonth[0]);
                increaseDecreaseQuery.setPlanStopMeMonth(yearAndMonth[1]);
            } else {
                increaseDecreaseQuery.setStopMe(false);
            }
            if (importDecreaseDTO.getWhetherSealed()) {
                increaseDecreaseQuery.setStopPf(true);
                String[] yearAndMonth = importDecreaseDTO.getSealedDate().split("/");
                increaseDecreaseQuery.setSealedDateYear(yearAndMonth[0]);
                increaseDecreaseQuery.setSealedDateMonth(yearAndMonth[1]);
            } else {
                increaseDecreaseQuery.setStopPf(false);
            }
            if (NO_CONFIRM.equals(confirmSocBySite)) {
                decrease(increaseDecreaseQuery, tokenUser);
            } else {
                empSocInfoExtraService.decreaseExtra(increaseDecreaseQuery, tokenUser);
            }
        }
    }

    /**
     * 增员时计算金额
     */
    @Override
    public EmpSocPlanBatchAddDTO batchCalculationAmount(EmpSocPlanBatchAddDTO batchIncreaseDTO, SocProgDO socProgDO) {
        List<EmpSocPlanAddDTO> empSocPlanAddDTOList = batchIncreaseDTO.getEmpSocPlanAddDTOList();
        for (EmpSocPlanAddDTO empSocPlanAddDTO : empSocPlanAddDTOList) {
            List<EmpSocPlanItemAddDTO> empSocPlanItemList = empSocPlanAddDTO.getEmpSocPlanItemAddDTOList();
            BigDecimal personalSoc = new BigDecimal("0");
            BigDecimal companySoc = new BigDecimal("0");
            BigDecimal personalPf = new BigDecimal("0");
            BigDecimal companyPf = new BigDecimal("0");
            for (EmpSocPlanItemAddDTO empSocPlanItemAddDTO : empSocPlanItemList) {
                BigDecimal cardinalityDefault = empSocPlanItemAddDTO.getCardinalityDefault();
                if (cardinalityDefault.compareTo(empSocPlanItemAddDTO.getCardinalityMin()) < 0) {
                    empSocPlanItemAddDTO.setCardinalityDefault(empSocPlanItemAddDTO.getCardinalityMin());
                    cardinalityDefault = empSocPlanItemAddDTO.getCardinalityMin();
                } else if (empSocPlanItemAddDTO.getCardinalityMax().compareTo(cardinalityDefault) < 0) {
                    empSocPlanItemAddDTO.setCardinalityDefault(empSocPlanItemAddDTO.getCardinalityMax());
                    cardinalityDefault = empSocPlanItemAddDTO.getCardinalityMax();
                }
                BigDecimal company;
                BigDecimal personal;
                if (socProgDO.getOnlySocAmount() != null && socProgDO.getOnlySocAmount()) {
                    company = empSocPlanItemAddDTO.getAmountCompany();
                    personal = empSocPlanItemAddDTO.getAmountPersonal();
                } else {
                    company = cardinalityDefault.multiply(empSocPlanItemAddDTO.getPersentCompany()
                            .multiply(new BigDecimal("0.01")));
                    personal = cardinalityDefault.multiply(empSocPlanItemAddDTO.getPersentPersonal()
                            .multiply(new BigDecimal("0.01")));
                }
                company = empSocTransformService.mantissaProcessing(company, empSocPlanItemAddDTO.getMantissaProcCompany());
                personal = empSocTransformService.mantissaProcessing(personal, empSocPlanItemAddDTO.getMantissaProcPersonal());
                empSocPlanItemAddDTO.setAmountCompany(company);
                empSocPlanItemAddDTO.setAmountPersonal(personal);
                if (SOC_ITEM_TYPE.equals(empSocPlanAddDTO.getType()) ||
                        ME_ITEM_TYPE.equals(empSocPlanAddDTO.getType())) {
                    personalSoc = personalSoc.add(personal);
                    companySoc = companySoc.add(company);
                } else if (PF_ITEM_TYPE.equals(empSocPlanAddDTO.getType())) {
                    personalPf = personalPf.add(personal);
                    companyPf = companyPf.add(company);
                } else {
                    throw new ParamException("社保子项类型错误");
                }
            }
            empSocPlanAddDTO.setPersonalPfAmount(personalPf);
            empSocPlanAddDTO.setPersonalSocAmount(personalSoc);
            empSocPlanAddDTO.setCompanyPfAmount(companyPf);
            empSocPlanAddDTO.setCompanySocAmount(companySoc);
        }
        return batchIncreaseDTO;

    }

    /**
     * 增员反馈合并数据
     */
    private void mergeAmount(EmpSocInfoDO empSocInfoDO, EmpSocPlanDO empSocPlanDO) {
        if (SOC_ITEM_TYPE.equals(empSocPlanDO.getType()) || ME_ITEM_TYPE.equals(empSocPlanDO.getType())) {
            empSocInfoDO.setPersonalSocAmount(empSocInfoDO.getPersonalSocAmount().add(empSocPlanDO.getPersonalSocAmount()));
            empSocInfoDO.setCompanySocAmount(empSocInfoDO.getCompanySocAmount().add(empSocPlanDO.getCompanySocAmount()));
        } else if (PF_ITEM_TYPE.equals(empSocPlanDO.getType())) {
            empSocInfoDO.setPersonalPfAmount(empSocInfoDO.getPersonalPfAmount().add(empSocPlanDO.getPersonalPfAmount()));
            empSocInfoDO.setCompanyPfAmount(empSocInfoDO.getCompanyPfAmount().add(empSocPlanDO.getCompanyPfAmount()));
        } else {
            throw new ParamException("子项类型错误");
        }
        if (!empSocPlanDO.getSchemeId().equals(empSocInfoDO.getProgId())) {
            empSocInfoDO.setProgId(empSocPlanDO.getSchemeId());
        }
        empSocInfoDO.setServiceCost(empSocPlanDO.getServiceCost());
    }

    @Override
    public void increaseMerge(List<EmpSocPlanItemDO> planItemList, EmpSocMonthDO empSocMonth, EmpInfoDO empInfo,
                              EmpSocPlanDO empSocPlanDO, TokenUser tokenUser, Boolean future) {
        EmpSocInfoDO empSocInfoDO = empSocInfoService
                .selectInfoByMonthId(empSocPlanDO.getEmpId(), empSocMonth.getId(), tokenUser);
        String empSocInfoId;
        if (!ObjectUtils.isEmpty(empSocInfoDO)) {
            mergeAmount(empSocInfoDO, empSocPlanDO);
            updateEmpSocStatusByType(empSocPlanDO.getType(), EmpSocStatus.INCREMENT.getValue(),
                    EmpSocStatus.NORMAL_SOC.getValue(), future, empSocInfoDO);
            empSocInfoService.updateByIdSelective(empSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
            empSocInfoId = empSocInfoDO.getId();
        } else {
            EmpSocInfoDO saveEmpSocInfoDO = empSocPlanDO.to(EmpSocInfoDO.class);
            saveEmpSocInfoDO.setId(null);
            saveEmpSocInfoDO.setYear(empSocMonth.getYear());
            saveEmpSocInfoDO.setMonth(empSocMonth.getMonth());
            saveEmpSocInfoDO.setProgId(empSocPlanDO.getSchemeId());
            saveEmpSocInfoDO.setPayCostType(SocPayCostType.NORMAL_PAY_COST.getValue());
            saveEmpSocInfoDO.setPlanFlag(true);
            saveEmpSocInfoDO.setMonthId(empSocMonth.getId());
            // 判断一下这个月这个人是否已离职
            checkLeaveDate(empInfo, saveEmpSocInfoDO, empSocMonth.getYear(), empSocMonth.getMonth());
            updateEmpSocStatusByType(empSocPlanDO.getType(), EmpSocStatus.INCREMENT.getValue(),
                    EmpSocStatus.NORMAL_SOC.getValue(), future, saveEmpSocInfoDO);
            EmpSocInfoDO saveResult = empSocInfoService
                    .save(saveEmpSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
            empSocInfoId = saveResult.getId();
        }
        // 增员反馈的时候会莫名其妙的创建出重复项,这里做一下过滤
        List<EmpSocPlanItemDO> filterItemList = new ArrayList<>();
        Map<String, List<EmpSocPlanItemDO>> itemNameMap =
                planItemList.stream().collect(Collectors.groupingBy(EmpSocPlanItemDO::getItemName));
        for (String itemName : itemNameMap.keySet()) {
            List<EmpSocPlanItemDO> empSocPlanItem = itemNameMap.get(itemName);
            filterItemList.add(empSocPlanItem.get(0));
        }
        List<EmpSocItemDO> insetEmpSocItem = filterItemList.stream().map(empSocPlanItemDO -> {
            EmpSocItemDO empSocItemDO = empSocPlanItemDO.to(EmpSocItemDO.class);
            empSocItemDO.setId(null);
            empSocItemDO.setEmpSocId(empSocInfoId);
            empSocItemDO.setItemType(empSocPlanDO.getType());
            return empSocItemDO;
        }).collect(Collectors.toList());
        empSocItemDAO.insertListAndSetId(insetEmpSocItem);
    }

    /**
     * 合并增员计划到未来月的参保信息里
     */
    private void increaseFutureMerge(List<EmpSocPlanItemDO> planItemList, List<EmpSocMonthDO> empSocMonthList,
                                     EmpInfoDO empInfo, EmpSocPlanDO empSocPlan, TokenUser tokenUser) {
        for (EmpSocMonthDO empSocMonth : empSocMonthList) {
            increaseMerge(planItemList, empSocMonth, empInfo, empSocPlan, tokenUser, true);
        }
    }

    @Override
    public void decreaseMerge(String monthId, EmpSocPlanDO empSocPlanDO, Boolean future, TokenUser tokenUser) {
        EmpSocInfoDO empSocInfoDO = empSocInfoService.selectInfoByMonthId(empSocPlanDO.getEmpId(), monthId, tokenUser);
        if (!ObjectUtils.isEmpty(empSocInfoDO)) {
            List<EmpSocItemDO> empSocItemList = empSocItemService
                    .selectByEmpSocIdAndItemType(empSocInfoDO.getId(), empSocPlanDO.getType(), tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(empSocItemList)) {
                BigDecimal personalAmount = new BigDecimal("0");
                BigDecimal companyAmount = new BigDecimal("0");
                for (EmpSocItemDO empSocItemDO : empSocItemList) {
                    personalAmount = personalAmount.add(empSocItemDO.getAmountPersonal());
                    companyAmount = companyAmount.add(empSocItemDO.getAmountCompany());
                }
                empSocItemService.deleteByEmpSocInfoIdAndItemType(empSocInfoDO.getId(),
                        empSocPlanDO.getType(), tokenUser.getSiteId());
                if (SOC_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                    empSocInfoDO.setCompanySocAmount(empSocInfoDO.getCompanySocAmount().subtract(companyAmount));
                    empSocInfoDO.setPersonalSocAmount(empSocInfoDO.getPersonalSocAmount().subtract(personalAmount));
                    if (future) {
                        empSocInfoDO.setEmpSocStatus(STOP_SOC.getValue());
                    } else {
                        empSocInfoDO.setEmpSocStatus(ATTRITION.getValue());
                    }
                } else if (ME_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                    empSocInfoDO.setCompanySocAmount(empSocInfoDO.getCompanySocAmount().subtract(companyAmount));
                    empSocInfoDO.setPersonalSocAmount(empSocInfoDO.getPersonalSocAmount().subtract(personalAmount));
                    if (future) {
                        empSocInfoDO.setEmpMeStatus(STOP_SOC.getValue());
                    } else {
                        empSocInfoDO.setEmpMeStatus(ATTRITION.getValue());
                    }
                } else if (PF_ITEM_TYPE.equals(empSocPlanDO.getType())) {
                    empSocInfoDO.setCompanyPfAmount(empSocInfoDO.getCompanyPfAmount().subtract(companyAmount));
                    empSocInfoDO.setPersonalPfAmount(empSocInfoDO.getPersonalPfAmount().subtract(personalAmount));
                    if (future) {
                        empSocInfoDO.setEmpPfStatus(STOP_SOC.getValue());
                    } else {
                        empSocInfoDO.setEmpPfStatus(ATTRITION.getValue());
                    }
                } else {
                    throw new ParamException("子项类型错误");
                }
                if (!ObjectUtils.isEmpty(empSocPlanDO.getServiceCost())) {
                    empSocInfoDO.setServiceCost(empSocPlanDO.getServiceCost());
                }
                empSocInfoService.updateByIdSelective(empSocInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
            }
        }
    }

    /**
     * 根据类型更新社保或者医保状态
     */
    private void updateEmpSocStatusByType(Integer type, Integer incrementOrDecrease, Integer futureStatus,
                                          Boolean future, EmpSocInfoDO empSocInfoDO) {
        if (SOC_ITEM_TYPE.equals(type)) {
            if (future) {
                empSocInfoDO.setEmpSocStatus(futureStatus);
            } else {
                empSocInfoDO.setEmpSocStatus(incrementOrDecrease);
            }
        } else if (ME_ITEM_TYPE.equals(type)) {
            if (future) {
                empSocInfoDO.setEmpMeStatus(futureStatus);
            } else {
                empSocInfoDO.setEmpMeStatus(incrementOrDecrease);
            }
        } else if (PF_ITEM_TYPE.equals(type)) {
            if (future) {
                empSocInfoDO.setEmpPfStatus(futureStatus);
            } else {
                empSocInfoDO.setEmpPfStatus(incrementOrDecrease);
            }
        } else {
            throw new ParamException("子项类型错误");
        }
    }

    @Override
    public EmpSocPlanDO initDecreasePlanByType(String companyId, Integer type, String year, String month,
                                               String empId, BigDecimal serviceCost, TokenUser tokenUser) {
        // 判断一下是不是待增
        boolean plan = !CollectionUtils.isEmpty(DateUtils.getMonthDifferences(new Date(),
                DateKit8.parseDate(year + "-" + month + "-01")));
        EmpSocPlanDO empSocPlanDO = new EmpSocPlanDO();
        empSocPlanDO.setEmpId(empId);
        empSocPlanDO.setType(type);
        empSocPlanDO.setFeedbackFlag(1);
        empSocPlanDO.setYear(year);
        empSocPlanDO.setMonth(month);
        empSocPlanDO.setCompanyId(companyId);
        if (plan) {
            empSocPlanDO.setEmpSocState(WAIT_ATTRITION.getValue());
        } else {
            empSocPlanDO.setEmpSocState(ATTRITION.getValue());
        }
        empSocPlanDO.preInsert(tokenUser.getUserId());
        empSocPlanDO.setSiteId(tokenUser.getSiteId());
        if (!ObjectUtils.isEmpty(serviceCost)) {
            empSocPlanDO.setServiceCost(serviceCost);
        }
        return empSocPlanDO;
    }

    @Override
    public EmpSocPlanDO initDecreasePlanByType2(String companyId, Integer type, String year, String month,
                                                String empId, BigDecimal serviceCost, TokenUser tokenUser) {
        // 判断一下是不是待增
        boolean plan = !CollectionUtils.isEmpty(DateUtils.getMonthDifferences(new Date(),
                DateKit8.parseDate(year + "-" + month + "-01")));
        EmpSocPlanDO empSocPlanDO = new EmpSocPlanDO();
        empSocPlanDO.setEmpId(empId);
        empSocPlanDO.setType(type);
        empSocPlanDO.setYear(year);
        empSocPlanDO.setMonth(month);
        empSocPlanDO.setCompanyId(companyId);
        if (plan) {
            empSocPlanDO.setEmpSocState(WAIT_ATTRITION.getValue());
        } else {
            empSocPlanDO.setEmpSocState(ATTRITION.getValue());
        }
        empSocPlanDO.preInsert(tokenUser.getUserId());
        empSocPlanDO.setSiteId(tokenUser.getSiteId());
        if (!ObjectUtils.isEmpty(serviceCost)) {
            empSocPlanDO.setServiceCost(serviceCost);
        }
        return empSocPlanDO;
    }

    @Override
    public List<EmpSocPlanDO> findNotFeedback(String empId, Integer type, String socType, TokenUser tokenUser) {
        List<Integer> increment = Arrays.asList(INCREMENT.getValue(), WAIT_INCREMENT.getValue());
        List<Integer> attrition = Arrays.asList(ATTRITION.getValue(), WAIT_ATTRITION.getValue());
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("empId", empId)
                .andEqualTo("type", type)
                .andIsNull("feedbackFlag")
                .andEqualTo("siteId", tokenUser.getSiteId());
        if (INCREASE_STR.equals(socType)) {
            example.and().andIn("empSocState", increment);
        } else {
            example.and().andIn("empSocState", attrition);
        }
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    public List<EmpSocPlanDO> selectNotFeedbackBySchemeId(String schemeId, String siteId) {
        String[] yearAndMonth = DateUtils.getYearAndMonth(new Date());
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andIsNull("feedbackFlag")
                .andEqualTo("schemeId", schemeId)
                .andEqualTo("siteId", siteId);
        return empSocPlanDAO.selectByExample(example);
    }

    @Override
    @Async
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void reAmount(String schemeId, Boolean onlySocAmount,
                         List<SocProgItemAddDTO> schemeItemList, BigDecimal serviceCost, TokenUser tokenUser) {
        List<EmpSocPlanDO> empSocPlanList = selectNotFeedbackBySchemeId(schemeId, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(empSocPlanList)) {
            return;
        }
        List<String> planIdList = empSocPlanList.stream().map(EmpSocPlanDO::getId).collect(Collectors.toList());
        Map<String, List<EmpSocPlanItemDO>> itemMap = empSocPlanItemService.selectByPlanIdList(planIdList, tokenUser);
        Map<String, SocProgItemAddDTO> schemeItemMap = schemeItemList.stream()
                .collect(Collectors.toMap(SocProgItemAddDTO::getItemName, v -> v));
        for (EmpSocPlanDO empSocPlan : empSocPlanList) {
            List<EmpSocPlanItemDO> planItemList = itemMap.get(empSocPlan.getId());
            if (CollectionUtils.isEmpty(planItemList)) {
                continue;
            }
            BigDecimal personalSocAmount = new BigDecimal("0");
            BigDecimal personalPfAmount = new BigDecimal("0");
            BigDecimal companySocAmount = new BigDecimal("0");
            BigDecimal companyPfAmount = new BigDecimal("0");
            for (EmpSocPlanItemDO empSocPlanItem : planItemList) {
                SocProgItemAddDTO schemeItem = schemeItemMap.get(empSocPlanItem.getItemName());
                if (onlySocAmount) {
                    empSocPlanItem.setCardinalityDefault(schemeItem.getCardinalityDefaultAmount());
                    empSocPlanItem.setAmountCompany(schemeItem.getAmountCompany());
                    empSocPlanItem.setAmountPersonal(schemeItem.getAmountPersonal());
                    empSocPlanItemService.updateByIdSelective(empSocPlanItem);
                } else {
                    BigDecimal oldAmountCompany = empSocPlanItem.getAmountCompany();
                    BigDecimal oldAmountPersonal = empSocPlanItem.getAmountPersonal();
                    BigDecimal oldCardinalityMin = empSocPlanItem.getCardinalityMin();
                    BigDecimal oldCardinalityMax = empSocPlanItem.getCardinalityMax();
                    BigDecimal cardinalityDefault = empSocPlanItem.getCardinalityDefault();
                    if (empSocPlanItem.getCardinalityDefault().compareTo(schemeItem.getCardinalityMax()) > 0) {
                        cardinalityDefault = schemeItem.getCardinalityMax();
                    }
                    if (empSocPlanItem.getCardinalityDefault().compareTo(schemeItem.getCardinalityMin()) < 0) {
                        cardinalityDefault = schemeItem.getCardinalityMin();
                    }
                    BigDecimal persentCompany = schemeItem.getPersentCompany();
                    BigDecimal persentPersonal = schemeItem.getPersentPersonal();
                    empSocPlanItem.setCardinalityDefault(cardinalityDefault);
                    empSocPlanItem.setPersentCompany(persentCompany);
                    empSocPlanItem.setCardinalityMax(schemeItem.getCardinalityMax());
                    empSocPlanItem.setCardinalityMin(schemeItem.getCardinalityMin());
                    empSocPlanItem.setMantissaProcCompany(schemeItem.getMantissaProcCompany());
                    empSocPlanItem.setMantissaProcPersonal(schemeItem.getMantissaProcPersonal());
                    empSocPlanItem.setPersentPersonal(persentPersonal);
                    BigDecimal newAmountPersonal = cardinalityDefault
                            .multiply(persentPersonal.multiply(new BigDecimal("0.01")));
                    newAmountPersonal = empSocTransformService
                            .mantissaProcessing(newAmountPersonal, schemeItem.getMantissaProcPersonal());
                    empSocPlanItem.setAmountPersonal(newAmountPersonal);
                    BigDecimal newAmountCompany = cardinalityDefault
                            .multiply(persentCompany.multiply(new BigDecimal("0.01")));
                    newAmountCompany = empSocTransformService
                            .mantissaProcessing(newAmountCompany, schemeItem.getMantissaProcCompany());
                    empSocPlanItem.setAmountCompany(newAmountCompany);
                    // 如果他俩金额不相等再更新
                    boolean amountEqual = oldAmountCompany.compareTo(newAmountCompany) != 0 ||
                            oldAmountPersonal.compareTo(newAmountPersonal) != 0;
                    boolean defaultEqual = oldCardinalityMin.compareTo(schemeItem.getCardinalityMin()) != 0 ||
                            oldCardinalityMax.compareTo(schemeItem.getCardinalityMax()) != 0;
                    if (amountEqual || defaultEqual) {
                        empSocPlanItemService.updateByIdSelective(empSocPlanItem);
                    }
                }
                if (SOC_ITEM_TYPE.equals(schemeItem.getItemType()) || ME_ITEM_TYPE.equals(schemeItem.getItemType())) {
                    personalSocAmount = personalSocAmount.add(empSocPlanItem.getAmountPersonal());
                    companySocAmount = companySocAmount.add(empSocPlanItem.getAmountCompany());
                } else {
                    personalPfAmount = personalPfAmount.add(empSocPlanItem.getAmountPersonal());
                    companyPfAmount = companyPfAmount.add(empSocPlanItem.getAmountCompany());
                }
            }
            if (personalSocAmount.compareTo(empSocPlan.getPersonalSocAmount()) != 0 ||
                    personalPfAmount.compareTo(empSocPlan.getPersonalPfAmount()) != 0 ||
                    companyPfAmount.compareTo(empSocPlan.getCompanyPfAmount()) != 0 ||
                    companySocAmount.compareTo(empSocPlan.getCompanySocAmount()) != 0 ||
                    serviceCost.compareTo(empSocPlan.getServiceCost()) !=0) {
                empSocPlan.setPersonalSocAmount(personalSocAmount);
                empSocPlan.setPersonalPfAmount(personalPfAmount);
                empSocPlan.setCompanySocAmount(companySocAmount);
                empSocPlan.setCompanyPfAmount(companyPfAmount);
                empSocPlan.setServiceCost(serviceCost);
                updateByIdSelective(empSocPlan);
            }
        }
    }

    /**
     * 校验增员时有没有重复增员
     */
    private void checkRepeatIncrease(Map<String, EmpSocRelatedDO> empSocRelatedMap, Map<String, EmpInfoDO> empInfoMap,
                                     List<EmpSocPlanAddDTO> addPlanList) {
        List<Integer> checkStateList = Arrays.asList(INCREMENT.getValue(),
                WAIT_INCREMENT.getValue(), NORMAL_SOC.getValue());
        for (EmpSocPlanAddDTO empSocPlanAddDTO : addPlanList) {
            EmpInfoDO empInfo = empInfoMap.get(empSocPlanAddDTO.getEmpId());
            EmpSocRelatedDO empSocRelated = empSocRelatedMap.get(empSocPlanAddDTO.getEmpId());
            Integer type = empSocPlanAddDTO.getType();
            if (SOC_ITEM_TYPE.equals(type)) {
                Integer socState = empSocRelated.getSocState();
                if (checkStateList.contains(socState)) {
                    throw new ParamException("员工" + empInfo.getName() + SOC_STR + "重复增员");
                }
            } else if (ME_ITEM_TYPE.equals(type)) {
                Integer meState = empSocRelated.getMeState();
                if (checkStateList.contains(meState)) {
                    throw new ParamException("员工" + empInfo.getName() + ME_STR + "重复增员");
                }
            } else {
                Integer pfState = empSocRelated.getPfState();
                if (checkStateList.contains(pfState)) {
                    throw new ParamException("员工" + empInfo.getName() + PF_STR + "重复增员");
                }
            }
        }
    }

    /**
     * 导入增员把他键入的增员类型换成 Integer
     */
    private Integer importTypeConverted(String typeStr) {
        if ("社保增员".equals(typeStr)) {
            return 1;
        } else if ("医保增员".equals(typeStr)) {
            return 3;
        } else {
            return 2;
        }
    }

    /**
     * 根据查询参数，构建example
     *
     * @param empSocPlanQuery 查询参数
     * @param siteId          所属站点id
     * @return example
     */
    private Example buildExample(EmpSocPlanQuery empSocPlanQuery, String siteId) {
        Example example = new Example(EmpSocPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        // 排序
        ExampleKit.setExampleOrder(example, empSocPlanQuery.getOrders());
        return example;
    }

    /**
     * 校验员工是否离职
     */
    private void checkLeaveDate(EmpInfoDO empInfo, EmpSocInfoDO empSocInfo, String year, String month) {
        // 判断一下这个月这个人是否已离职
        Date leaveDate = empInfo.getLeaveDate();
        if (!ObjectUtils.isEmpty(leaveDate) && empInfo.getStatus() == 4) {
            // 判断一下离职时间
            String leaveDateStr = DateUtils.getYearMonthStr(leaveDate);
            if (Integer.parseInt(leaveDateStr) <= Integer.parseInt(year + month)) {
                empSocInfo.setWhetherLeave(true);
            }
        } else {
            empSocInfo.setWhetherLeave(false);
        }
    }
}




